# Create React App

本章节介绍如何将 [Create React App](https://github.com/facebook/create-react-app)（简称 CRA）或 [CRACO](https://craco.js.org/) 项目迁移到 Rsbuild。

:::tip CRA eject
如果你的项目已经执行了 CRA 的 `eject` 命令，那么本文档的大部分内容将不再适用。

CRA 项目在 eject 后，更近似于直接使用 webpack 的项目，因此你可以参考 [webpack 迁移指南](/guide/migration/webpack)。

:::

## 安装依赖

首先你需要把 CRA 的 npm 依赖替换为 Rsbuild 的依赖。

import { PackageManagerTabs } from '@theme';

- 移除 CRA 的依赖：

<PackageManagerTabs command="remove react-scripts" />

> 对于使用 CRACO 的项目，你还可以移除 `@craco/craco` 依赖。

- 安装 Rsbuild 的依赖：

<PackageManagerTabs command="add @rsbuild/core @rsbuild/plugin-react -D" />

## 更新 npm scripts

下一步，你需要把 package.json 中的 npm scripts 更新为 Rsbuild 的 CLI 命令。

```diff title="package.json"
{
  "scripts": {
-   "start": "react-scripts start",
-   "build": "react-scripts build",
-   "eject": "react-scripts eject"
+   "start": "rsbuild dev",
+   "build": "rsbuild build",
+   "preview": "rsbuild preview"
  }
}
```

:::tip
Rsbuild 未集成测试框架，因此没有提供用于替换 `react-scripts test` 的命令，你可以直接使用 Jest 或 Vitest 等测试框架。你可以参考 [Rsbuild react-jest](https://github.com/rspack-contrib/rspack-examples/tree/main/rsbuild/react-jest) 示例项目进行配置。
:::

## 创建配置文件

在 package.json 的同级目录下创建 Rsbuild 的配置文件 `rsbuild.config.ts`，并添加以下内容：

```ts title="rsbuild.config.ts"
import { defineConfig } from '@rsbuild/core';
import { pluginReact } from '@rsbuild/plugin-react';

export default defineConfig({
  plugins: [pluginReact()],
});
```

## HTML 模板

CRA 默认使用 `public/index.html` 文件作为 HTML 模板。在 Rsbuild 中，你可以通过 [html.template](/config/html/template) 来指定 HTML 模板：

```ts title="rsbuild.config.ts"
export default defineConfig({
  html: {
    template: './public/index.html',
  },
});
```

在 HTML 模板中，如果使用了 CRA 的 `%PUBLIC_URL%` 变量，请替换为 Rsbuild 的 [assetPrefix 变量](/config/html/template-parameters)，并使用斜杠进行连接：

```diff
-  <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
+  <link rel="icon" href="<%= assetPrefix %>/favicon.ico" />
```

这样就完成了从 CRA 到 Rsbuild 的基本迁移，此时你可以执行 `npm run start` 命令来尝试启动开发服务器。

## 构建产物目录

CRA 默认会将构建产物输出到 `build` 目录，而 Rsbuild 的默认构建产物目录为 `dist`。

你可以配置 Rsbuild 的 [output.distPath.root](/config/output/dist-path) 选项，将目录修改为 `build`，与 CRA 保持一致：

```ts title="rsbuild.config.ts"
export default {
  output: {
    distPath: {
      root: 'build',
    },
  },
};
```

> 更多细节请参考[构建产物目录](/guide/basic/output-files)章节。

## 使用 CSS 预处理器

Rsbuild 通过插件来支持 Sass 和 Less 等 CSS 预处理器，使用方式请参考：

- [Sass 插件](/plugins/list/plugin-sass)
- [Less 插件](/plugins/list/plugin-less)
- [Stylus 插件](/plugins/list/plugin-stylus)

## 使用 SVGR

如果你使用了 CRA 的 "SVG 转 React 组件" 功能（即 [SVGR](https://react-svgr.com/)），那么还需要安装 Rsbuild 的 SVGR 插件。

比如使用了以下用法：

```jsx
import { ReactComponent as Logo } from './logo.svg';

const App = () => (
  <div>
    <Logo />
  </div>
);
```

只需要安装和注册 `@rsbuild/plugin-svgr` 即可：

```ts title="rsbuild.config.ts"
import { pluginSvgr } from '@rsbuild/plugin-svgr';

export default {
  plugins: [pluginSvgr({ mixedImport: true })],
};
```

请参考 [SVGR 插件](/plugins/list/plugin-svgr) 文档了解如何在 Rsbuild 中使用 SVGR。

## 配置迁移

以下是 [CRA 配置](https://create-react-app.dev/docs/advanced-configuration/)对应的 Rsbuild 配置：

| CRA                     | Rsbuild                                                                                         |
| ----------------------- | ----------------------------------------------------------------------------------------------- |
| HOST                    | [server.host](/config/server/host)                                                              |
| PORT                    | [server.port](/config/server/port)                                                              |
| HTTPS                   | [server.https](/config/server/https)                                                            |
| WDS_SOCKET_HOST         | [dev.client.host](/config/dev/client)                                                           |
| WDS_SOCKET_PATH         | [dev.client.path](/config/dev/client)                                                           |
| WDS_SOCKET_PORT         | [dev.client.port](/config/dev/client)                                                           |
| PUBLIC_URL              | [dev.assetPrefix](/config/dev/asset-prefix) / [output.assetPrefix](/config/output/asset-prefix) |
| BUILD_PATH              | [output.distPath](/config/output/dist-path)                                                     |
| GENERATE_SOURCEMAP      | [output.sourceMap](/config/output/source-map)                                                   |
| IMAGE_INLINE_SIZE_LIMIT | [output.dataUriLimit](/config/output/data-uri-limit)                                            |
| FAST_REFRESH            | [dev.hmr](/config/dev/hmr)                                                                      |
| TSC_COMPILE_ON_ERROR    | [Type Check Plugin](/plugins/list/plugin-type-check)                                            |

说明：

- 上述表格尚未覆盖到 CRA 的所有配置，欢迎补充。

## 编译 node_modules

CRA 默认会使用 Babel 编译 node_modules 中的依赖，但 Rsbuild 则不会，这是为了避免二次编译带来的性能损耗和潜在的编译错误。

如果你需要处理 node_modules 中依赖引起的语法兼容性问题，可以使用 [source.include](/config/source/include#编译-node_modules) 配置项来编译 node_modules。

## 环境变量

CRA 默认会将 `REACT_APP_` 开头的环境变量注入到 client 代码中，而 Rsbuild 默认会注入 `PUBLIC_` 开头的环境变量（参考 [public 变量](/guide/advanced/env-vars#public-变量)）。

为了兼容 CRA 的行为，你可以手动调用 Rsbuild 提供的 [loadEnv](/api/javascript-api/core#loadenv) 方法来读取 `REACT_APP_` 开头的环境变量，并通过 [source.define](/config/source/define) 配置项注入到 client 代码中。

```ts title="rsbuild.config.ts"
import { defineConfig, loadEnv } from '@rsbuild/core';

const { publicVars } = loadEnv({ prefixes: ['REACT_APP_'] });

export default defineConfig({
  source: {
    define: publicVars,
  },
});
```

## 引用未知资源

在 CRA 中，如果你引用了一个构建工具无法识别的资源，CRA 默认会将该文件输出到 `build/static/media` 目录下，比如 `document.pdf` 文件：

```js title="index.js"
import document from './document.pdf';
```

在 Rsbuild 中，遇到无法识别的资源时，Rsbuild 会输出报错日志，类似于：

```text
You may need an appropriate loader to handle this file type.
```

为了解决该错误，你可以采用以下方法：

- 使用 [tools.rspack](/config/tools/rspack) 配置一个合适的 loader 来处理该类型的资源。
- 使用 [tools.rspack](/config/tools/rspack) 配置 [asset modules](https://rspack.dev/guide/features/asset-module) 规则来处理该类型的资源。

比如，你可以添加以下 asset modules 配置，来保持与 CRA 相同的输出结果：

```ts title="rsbuild.config.ts"
export default {
  tools: {
    rspack: {
      module: {
        rules: [
          {
            // 匹配 .png 文件
            // 你可以调整该正则表达式，以匹配不同类型的文件
            test: /\.png$/,
            type: 'asset/resource',
            generator: {
              filename: 'static/media/[name].[hash][ext]',
            },
          },
        ],
      },
    },
  },
};
```

## 移除 react-app-polyfill

CRA 提供了 [react-app-polyfill](https://www.npmjs.com/package/react-app-polyfill) 来手动注入 polyfill 代码。

在 Rsbuild 项目中，你可以移除 react-app-polyfill 相关的依赖和代码，因为 Rsbuild 会自动读取 browserslist 配置，并按需注入 polyfill 代码。

```diff title="src/index.js"
- import 'react-app-polyfill/ie11';
- import 'react-app-polyfill/stable';
```

你可以参考 [浏览器兼容性](/guide/advanced/browser-compatibility) 来了解 Rsbuild 是如何处理 polyfill 的。

## 读取 jsconfig.json

在非 TypeScript 项目中，CRA 支持读取 jsconfig.json 中的 `paths` 字段作为路径别名。

如果你需要在 Rsbuild 中使用该特性，可以使用 [source.tsconfigPath](/config/source/tsconfig-path) 选项，使 Rsbuild 能够识别 jsconfig.json 中的 `paths` 字段。

```js title="rsbuild.config.mjs"
export default {
  source: {
    tsconfigPath: './jsconfig.json',
  },
};
```

## CRACO 迁移

如果你的项目使用了 [CRACO](https://craco.js.org) 来覆盖 CRA 的配置，可以参考下表来迁移：

| CRACO                                                                                           | Rsbuild                                                          |
| ----------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
| [webpack.configure](https://craco.js.org/docs/configuration/webpack/#webpackconfigure)          | [tools.rspack](/config/tools/rspack)                             |
| [webpack.alias](https://craco.js.org/docs/configuration/webpack/#webpackalias)                  | [source.alias](/config/source/alias)                             |
| [webpack.plugins](https://craco.js.org/docs/configuration/webpack/#webpackplugins)              | [tools.rspack.appendPlugins](/config/tools/rspack#appendplugins) |
| [webpack.plugins.remove](https://craco.js.org/docs/configuration/webpack/#webpackpluginsremove) | [tools.rspack.removePlugin](/config/tools/rspack#removeplugin)   |
| [style.modules](https://craco.js.org/docs/configuration/style/#stylemodules)                    | [output.cssModules](/config/output/css-modules)                  |
| [style.css](https://craco.js.org/docs/configuration/style/#stylecss)                            | [tools.cssLoader](/config/tools/css-loader)                      |
| [style.sass](https://craco.js.org/docs/configuration/style/#stylesass)                          | [Sass 插件](/plugins/list/plugin-sass)                           |
| [style.postcss](https://craco.js.org/docs/configuration/style/#stylepostcss)                    | [tools.postcss](/config/tools/postcss)                           |
| [babel](https://craco.js.org/docs/configuration/babel/)                                         | [Babel 插件](/plugins/list/plugin-babel)                         |
| [typescript](https://craco.js.org/docs/configuration/typescript/)                               | [Type Check Plugin](/plugins/list/plugin-type-check)             |
| [devServer](https://craco.js.org/docs/configuration/devserver/)                                 | [server configs](/config/index)                                  |

### 示例

下面是一个从 `webpack.configure` 迁移到 `tools.rspack` 的例子：

- 迁移前：

```js title="craco.config.js"
const { whenDev } = require('@craco/craco');

module.exports = {
  webpack: {
    configure: {
      resolve: {
        mainFields: ['browser', 'module', 'main'],
      },
    },
    plugins: [...whenDev(() => [new MyWebpackPlugin()], [])],
  },
};
```

- 迁移后：

```ts title="rsbuild.config.ts"
export default {
  tools: {
    rspack: {
      resolve: {
        mainFields: ['browser', 'module', 'main'],
      },
      plugins:
        process.env.NODE_ENV === 'development' ? [new MyWebpackPlugin()] : [],
    },
  },
};
```

## 内容补充

当前文档只涵盖了迁移过程的部分事项，如果你发现有合适的内容可以补充，欢迎通过 pull request 来完善文档 🤝。

> Rsbuild 的文档位于 [rsbuild/website](https://github.com/web-infra-dev/rsbuild/tree/main/website) 目录。
